﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using MugenMvvmToolkit.Infrastructure;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkitTest.TestInfrastructure;
using MugenMvvmToolkitTest.TestModels;
using Should;

namespace MugenMvvmToolkitTest.Infrastructure
{
    public class ServiceProvider : IServiceProvider
    {
        #region Properties

        public TestValidationProvider ValidationProvider { get; set; }

        public TestThreadManager ThreadManager { get; set; }

        #endregion

        #region Implementation of IServiceProvider

        /// <summary>
        ///     Gets the service object of the specified type.
        /// </summary>
        /// <returns>
        ///     A service object of type <paramref name="serviceType" />.
        ///     -or-
        ///     null if there is no service object of type <paramref name="serviceType" />.
        /// </returns>
        /// <param name="serviceType">
        ///     An object that specifies the type of service object to get.
        /// </param>
        public object GetService(Type serviceType)
        {
            if (serviceType == typeof(IValidationProvider))
                return ValidationProvider;
            return ThreadManager;
        }

        #endregion
    }

    //TODO REWRITE ALL TESTS
    [TestClass]
    public class ValidationProviderValidatorTest : ValidatorBaseTest
    {
        #region Fields

        public const string NameProp = "Name";
        public const string MappingNameProp = "MappingName";

        public const string ValidatorError2 = "ValidatorError2";

        #endregion

        #region Tests

        [TestInitializeAttribute]
        public void TestInitialize()
        {
            ValidationProvider = new TestValidationProvider();
            ThreadManager = new TestThreadManager();
        }

        [TestMethod]
        public void ValidateAllAsyncTest()
        {
            var model = new TestValidatableModel();
            var validator = GetValidationProviderValidator();
            validator.Initialize(new ValidatorContext(model, GetServiceProvider()));

            validator.IsAsyncValidateAll = true;
            ValidationProvider.TypeToValidate = typeof(TestValidatableModel);
            ValidationProvider.Errors.Add(NameProp,
                                          new List<IValidationResult>
                                              {
                                                  new TestValidationResult(ValidatorError, NameProp)
                                              });

            ValidationProvider.Errors.Add(MappingNameProp,
                                          new List<IValidationResult>
                                              {
                                                  new TestValidationResult(
                                                      ValidatorError2, MappingNameProp)
                                              });

            ThreadManager.InvokeAsyncAction.ShouldBeNull();
#if TASKSUPPORT
            var task = validator.ValidateAll();
#else
            validator.ValidateAll();
#endif

            ThreadManager.InvokeAsyncAction.ShouldNotBeNull();
            ThreadManager.InvokeAsyncAction(ThreadManager.InvokeAsyncState);

#if TASKSUPPORT
            task.Wait();
#endif
            ValidationProvider.ValidateObjects.Contains(model).ShouldBeTrue();

            validator.GetErrors(NameProp).Cast<TestValidationResult>().Count().ShouldEqual(1);
            validator.GetErrors(MappingNameProp).Cast<TestValidationResult>().Count().ShouldEqual(1);
            validator.GetErrors(NameProp)
                     .OfType<TestValidationResult>()
                     .First()
                     .ErrorMessage.ShouldEqual(ValidatorError);
            validator.GetErrors(MappingNameProp)
                     .OfType<TestValidationResult>()
                     .First()
                     .ErrorMessage.ShouldEqual(ValidatorError2);
        }

        [TestMethod]
        public void ValidateAllTest()
        {
            var model = new TestValidatableModel();
            var validator = GetValidationProviderValidator();
            validator.Initialize(new ValidatorContext(model, GetServiceProvider()));

            ValidationProvider.TypeToValidate = typeof(TestValidatableModel);
            ValidationProvider.Errors.Add(NameProp,
                                          new List<IValidationResult>
                                              {
                                                  new TestValidationResult(ValidatorError, NameProp)
                                              });

            ValidationProvider.Errors.Add(MappingNameProp,
                                          new List<IValidationResult>
                                              {
                                                  new TestValidationResult(
                                                      ValidatorError2, MappingNameProp)
                                              });
            validator.ValidateAll();
            ValidationProvider.ValidateObjects.Contains(model).ShouldBeTrue();

            validator.GetErrors(NameProp).Cast<TestValidationResult>().Count().ShouldEqual(1);
            validator.GetErrors(MappingNameProp).Cast<TestValidationResult>().Count().ShouldEqual(1);
            validator.GetErrors(NameProp)
                     .OfType<TestValidationResult>()
                     .First()
                     .ErrorMessage.ShouldEqual(ValidatorError);
            validator.GetErrors(MappingNameProp)
                     .OfType<TestValidationResult>()
                     .First()
                     .ErrorMessage.ShouldEqual(ValidatorError2);
        }

        [TestMethod]
        public void ValidateAllShouldRemoveOldErrorsTest()
        {
            var model = new TestValidatableModel();
            var validator = GetValidationProviderValidator();
            validator.Initialize(new ValidatorContext(model, GetServiceProvider()));

            ValidationProvider.TypeToValidate = typeof(TestValidatableModel);
            ValidationProvider.Errors.Add(NameProp,
                                          new List<IValidationResult>
                                              {
                                                  new TestValidationResult(ValidatorError, NameProp)
                                              });

            ValidationProvider.Errors.Add(MappingNameProp,
                                          new List<IValidationResult>
                                              {
                                                  new TestValidationResult(
                                                      ValidatorError2, MappingNameProp)
                                              });
            validator.ValidateAll();
            ValidationProvider.ValidateObjects.Contains(model).ShouldBeTrue();

            validator.GetErrors(NameProp).Cast<TestValidationResult>().Count().ShouldEqual(1);
            validator.GetErrors(MappingNameProp).Cast<TestValidationResult>().Count().ShouldEqual(1);
            validator.GetErrors(NameProp)
                     .OfType<TestValidationResult>()
                     .First()
                     .ErrorMessage.ShouldEqual(ValidatorError);
            validator.GetErrors(MappingNameProp)
                     .OfType<TestValidationResult>()
                     .First()
                     .ErrorMessage.ShouldEqual(ValidatorError2);

            ValidationProvider.Errors.Clear();

            validator.ValidateAll();
            validator.GetErrors(NameProp).ShouldBeEmpty();
            validator.GetErrors(MappingNameProp).ShouldBeEmpty();
        }

        [TestMethod]
        public void ValidateTest()
        {
            var model = new TestValidatableModel();
            var validator = GetValidationProviderValidator();
            validator.Initialize(new ValidatorContext(model, GetServiceProvider()));

            ValidationProvider.TypeToValidate = typeof(TestValidatableModel);
            ValidationProvider.Errors.Add(NameProp,
                                          new List<IValidationResult>
                                              {
                                                  new TestValidationResult(ValidatorError, NameProp)
                                              });

            ValidationProvider.Errors.Add(MappingNameProp,
                                          new List<IValidationResult>
                                              {
                                                  new TestValidationResult(
                                                      ValidatorError2, MappingNameProp)
                                              });
            validator.GetErrors(NameProp).ShouldBeEmpty();
            validator.GetErrors(MappingNameProp).ShouldBeEmpty();
            for (int i = 0; i < 10; i++)
            {
                validator.Validate(NameProp);
                validator.GetErrors(NameProp).Cast<TestValidationResult>().Count().ShouldEqual(1);
                validator.GetErrors(NameProp)
                         .OfType<TestValidationResult>()
                         .First()
                         .ErrorMessage.ShouldEqual(ValidatorError);

                validator.Validate(MappingNameProp);
                validator.GetErrors(MappingNameProp).Cast<TestValidationResult>().Count().ShouldEqual(1);
                validator.GetErrors(MappingNameProp)
                         .OfType<TestValidationResult>()
                         .First()
                         .ErrorMessage.ShouldEqual(ValidatorError2);
            }

            ValidationProvider.Errors.Clear();
            validator.Validate(NameProp);
            validator.Validate(MappingNameProp);
            validator.GetErrors(NameProp).ShouldBeEmpty();
            validator.GetErrors(MappingNameProp).ShouldBeEmpty();
        }

        [TestMethod]
        public void AsyncValidateTest()
        {
            var model = new TestValidatableModel();
            var validator = GetValidationProviderValidator();
            validator.Initialize(new ValidatorContext(model, GetServiceProvider()));

            ValidationProvider.TypeToValidate = typeof(TestValidatableModel);
            ValidationProvider.Errors.Add(NameProp,
                                          new List<IValidationResult>
                                              {
                                                  new TestValidationResult(ValidatorError, NameProp)
                                              });

            ValidationProvider.Errors.Add(MappingNameProp,
                                          new List<IValidationResult>
                                              {
                                                  new TestValidationResult(
                                                      ValidatorError2, MappingNameProp)
                                              });
            validator.GetErrors(NameProp).ShouldBeEmpty();
            validator.GetErrors(MappingNameProp).ShouldBeEmpty();
            validator.IsAsyncValidate = true;
            for (int i = 0; i < 10; i++)
            {
                ThreadManager.InvokeAsyncAction = null;
                ThreadManager.InvokeAsyncAction.ShouldBeNull();
#if TASKSUPPORT
                var task = validator.Validate(NameProp);
#else
                validator.Validate(NameProp);
#endif

                ThreadManager.InvokeAsyncAction.ShouldNotBeNull();
                ThreadManager.InvokeAsyncAction(ThreadManager.InvokeAsyncState);
#if TASKSUPPORT
                task.Wait();
#endif
                validator.GetErrors(NameProp).Cast<TestValidationResult>().Count().ShouldEqual(1);
                validator.GetErrors(NameProp)
                         .OfType<TestValidationResult>()
                         .First()
                         .ErrorMessage.ShouldEqual(ValidatorError);

                ThreadManager.InvokeAsyncAction = null;
                ThreadManager.InvokeAsyncAction.ShouldBeNull();
#if TASKSUPPORT
                task = validator.Validate(MappingNameProp);
#else
                validator.Validate(MappingNameProp);
#endif

                ThreadManager.InvokeAsyncAction.ShouldNotBeNull();
                ThreadManager.InvokeAsyncAction(ThreadManager.InvokeAsyncState);
#if TASKSUPPORT
                task.Wait();
#endif

                validator.GetErrors(MappingNameProp).Cast<TestValidationResult>().Count().ShouldEqual(1);
                validator.GetErrors(MappingNameProp)
                         .OfType<TestValidationResult>()
                         .First()
                         .ErrorMessage.ShouldEqual(ValidatorError2);
            }

            ValidationProvider.Errors.Clear();
            validator.IsAsyncValidate = false;
            validator.Validate(NameProp);
            validator.Validate(MappingNameProp);
            validator.GetErrors(NameProp).ShouldBeEmpty();
            validator.GetErrors(MappingNameProp).ShouldBeEmpty();
        }

        #endregion

        #region Properties

        protected TestValidationProvider ValidationProvider { get; set; }

        #endregion

        #region Methods

        protected virtual ValidationProviderValidator GetValidationProviderValidator()
        {
            return new ValidationProviderValidator();
        }

        #endregion

        #region Overrides of ValidatorBaseTest

        [TestMethod]
        public override void CloneTest()
        {
            base.CloneTest();
            var validator = GetValidationProviderValidator();
            validator.IsAsyncValidate.ShouldBeFalse();
            validator.IsAsyncValidateAll.ShouldBeFalse();

            var clone = (ValidationProviderValidator)validator.Clone();
            clone.IsAsyncValidate.ShouldBeFalse();
            clone.IsAsyncValidateAll.ShouldBeFalse();

            validator.IsAsyncValidate = true;
            validator.IsAsyncValidateAll = true;

            clone = (ValidationProviderValidator)validator.Clone();
            clone.IsAsyncValidate.ShouldBeTrue();
            clone.IsAsyncValidateAll.ShouldBeTrue();
        }

        protected override IServiceProvider GetServiceProvider()
        {
            return new ServiceProvider
                       {
                           ValidationProvider = ValidationProvider,
                           ThreadManager = ThreadManager
                       };
        }

        protected override ValidatorBase GetValidator()
        {
            return new ValidationProviderValidator();
        }

        #endregion
    }
}